package com.alogic.validator.impl.pwd;


import com.alogic.validator.impl.Similar;
import com.anysoft.util.Properties;
import com.anysoft.util.PropertiesConstants;
import org.apache.commons.collections4.SetUtils;
import org.apache.commons.lang3.StringUtils;

import java.util.*;

/**
 * 根据余弦相似性来判断相似性
 * @since 1.6.13.28 [20201218 duanyy] <br>
 */
public class Cos extends Similar {
    protected double threshold = 0.5f;

    @Override
    public void configure(Properties p) {
        super.configure(p);
        threshold = PropertiesConstants.getDouble(p,"threshold",0.5f,true);
    }

    @Override
    protected boolean isSimilar(String key, String value) {
        return cos(key,value) > threshold;
    }

    public static float cos(String a, String b) {
        if (StringUtils.isEmpty(a) && StringUtils.isEmpty(b)) {
            return 1f;
        }
        if (StringUtils.isEmpty(a) && StringUtils.isEmpty(b)) {
            return 0f;
        }
        Set<Character> aChar = chars(a);
        Set<Character> bChar = chars(b);
        // 统计字频
        Map<Character, Integer> aMap = new HashMap<>();
        Map<Character, Integer> bMap = new HashMap<>();
        for (Character a1 : aChar) {
            aMap.put(a1, get(aMap,a1, 0) + 1);
        }
        for (Character b1 : bChar) {
            bMap.put(b1, get(bMap,b1, 0) + 1);
        }

        Set<Character> union = SetUtils.union(aChar, bChar);
        int[] aVec = new int[union.size()];
        int[] bVec = new int[union.size()];
        List<Character> collect = new ArrayList<Character>(union);
        for (int i = 0; i < collect.size(); i++) {
            aVec[i] = get(aMap,collect.get(i), 0);
            bVec[i] = get(bMap,collect.get(i), 0);
        }

        int p1 = 0;
        for (int i = 0; i < aVec.length; i++) {
            p1 += (aVec[i] * bVec[i]);
        }

        float p2 = 0f;
        for (int i : aVec) {
            p2 += (i * i);
        }
        p2 = (float) Math.sqrt(p2);

        float p3 = 0f;
        for (int i : bVec) {
            p3 += (i * i);
        }
        p3 = (float) Math.sqrt(p3);

        return ((float) p1) / (p2 * p3);
    }

    public static int get(Map<Character,Integer> map,Character ch,int dft){
        Integer found = map.get(ch);
        return found != null?found:dft;
    }

    public static Set<Character> chars(String str){
        Set<Character> chars = new HashSet<Character>();
        for (int i = 0 ; i < str.length() ; i ++){
            chars.add(str.charAt(i));
        }
        return chars;
    }
}